#ifndef __RASPI4_H__
#define __RASPI4_H__
//https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf

#define __REG32(x)  (*((volatile unsigned int *)(x)))
#define __REG16(x)  (*((volatile unsigned short *)(x)))

/* GIC IRQ MAX */
#define MAX_HANDLERS                (256)

/* base address */
#define PER_BASE                        (0x3F000000)
#define PER_BASE_40000000               (0x40000000)

//gpio offset
#define GPIO_BASE_OFFSET            (0x00200000)


#define PL011_UART_BASE_OFFSET      (0x00201000)
//pl011 offset
#define PL011_UART0_BASE_OFFSET     (0x00201000)
#define PL011_UART2_BASE_OFFSET     (0x00201400)
#define PL011_UART3_BASE_OFFSET     (0x00201600)
#define PL011_UART4_BASE_OFFSET     (0x00201800)
#define PL011_UART5_BASE_OFFSET     (0x00201A00)

//pactl cs offset
#define PACTL_CS_OFFSET             (0x00204E00)

//aux offset
#define AUX_BASE_OFFSET             (0x00215000)

/* GPIO */
#define BCM283X_GPIO_BASE           (PER_BASE + GPIO_BASE_OFFSET)

#define IRQ_AUX      29

/* Interrupt Controler */
extern uint32_t irq_base;
#define IRQ_BASE            (PER_BASE + 0xB200)
#define IRQ_PEND_BASIC      HWREG32(irq_base + 0x0000)
#define IRQ_PEND1           HWREG32(irq_base + 0x0004)
#define IRQ_PEND2           HWREG32(irq_base + 0x0008)
#define IRQ_FIQ_CONTROL     HWREG32(irq_base + 0x000C)
#define IRQ_ENABLE1         HWREG32(irq_base + 0x0010)
#define IRQ_ENABLE2         HWREG32(irq_base + 0x0014)
#define IRQ_ENABLE_BASIC    HWREG32(irq_base + 0x0018)
#define IRQ_DISABLE1        HWREG32(irq_base + 0x001C)
#define IRQ_DISABLE2        HWREG32(irq_base + 0x0020)
#define IRQ_DISABLE_BASIC   HWREG32(irq_base + 0x0024)

extern uint32_t gpio_base_addr;
#define GPIO_BASE                   (gpio_base_addr)
#define GPIO_IRQ_NUM                (3)   //40 pin mode
#define IRQ_GPIO0                   (96 + 49) //bank0 (0 to 27)
#define IRQ_GPIO1                   (96 + 50) //bank1 (28 to 45)
#define IRQ_GPIO2                   (96 + 51) //bank2 (46 to 57)
#define IRQ_GPIO3                   (96 + 52) //bank3

/* Timer (ARM side) */
#define ARM_TIMER_IRQ       (64)
extern uint32_t arm_timer_base;
#define ARM_TIMER_BASE      (PER_BASE + 0xB000)
#define ARM_TIMER_LOAD      HWREG32(arm_timer_base + 0x400)
#define ARM_TIMER_VALUE     HWREG32(arm_timer_base + 0x404)
#define ARM_TIMER_CTRL      HWREG32(arm_timer_base + 0x408)
#define ARM_TIMER_IRQCLR    HWREG32(arm_timer_base + 0x40C)
#define ARM_TIMER_RAWIRQ    HWREG32(arm_timer_base + 0x410)
#define ARM_TIMER_MASKIRQ   HWREG32(arm_timer_base + 0x414)
#define ARM_TIMER_RELOAD    HWREG32(arm_timer_base + 0x418)
#define ARM_TIMER_PREDIV    HWREG32(arm_timer_base + 0x41C)
#define ARM_TIMER_CNTR      HWREG32(arm_timer_base + 0x420)



/* ARM Core Timer */
extern uint32_t per_base_40000000;
#define C0TIMER_INTCTL          HWREG32(per_base_40000000 + 0x40)  /* Core0 timers Interrupt control */
#define C1TIMER_INTCTL          HWREG32(per_base_40000000 + 0x44)  /* Core1 timers Interrupt control */
#define C2TIMER_INTCTL          HWREG32(per_base_40000000 + 0x48)  /* Core2 timers Interrupt control */
#define C3TIMER_INTCTL          HWREG32(per_base_40000000 + 0x4C)  /* Core3 timers Interrupt control */
#define CORETIMER_INTCTL(n)     HWREG32(per_base_40000000 + 0x40 + n*4)  /* Coren timers Interrupt control */

/* ARM Core Mailbox interrupt */
#define C0MB_INTCTL             HWREG32(per_base_40000000 + 0x50)  /* Core0 Mailboxes Interrupt control */
#define C1MB_INTCTL             HWREG32(per_base_40000000 + 0x54)  /* Core1 Mailboxes Interrupt control */
#define C2MB_INTCTL             HWREG32(per_base_40000000 + 0x58)  /* Core2 Mailboxes Interrupt control */
#define C3MB_INTCTL             HWREG32(per_base_40000000 + 0x5C)  /* Core3 Mailboxes Interrupt control */
#define COREMB_INTCTL(n)        HWREG32(per_base_40000000 + 0x50 + 4*n)  /* Coren Mailboxes Interrupt control */

/* ARM Core IRQ/FIQ status */
#define C0_IRQSOURCE            HWREG32(per_base_40000000 + 0x60)  /* Core0 IRQ Source */
#define C1_IRQSOURCE            HWREG32(per_base_40000000 + 0x64)  /* Core1 IRQ Source */
#define C2_IRQSOURCE            HWREG32(per_base_40000000 + 0x68)  /* Core2 IRQ Source */
#define C3_IRQSOURCE            HWREG32(per_base_40000000 + 0x6C)  /* Core3 IRQ Source */
#define C0_FIQSOURCE            HWREG32(per_base_40000000 + 0x70)  /* Core0 FIQ Source */
#define C1_FIQSOURCE            HWREG32(per_base_40000000 + 0x74)  /* Core1 FIQ Source */
#define C2_FIQSOURCE            HWREG32(per_base_40000000 + 0x78)  /* Core2 FIQ Source */
#define C3_FIQSOURCE            HWREG32(per_base_40000000 + 0x7C)  /* Core3 FIQ Source */
#define CORE_IRQSOURCE(n)       HWREG32(per_base_40000000 + 0x60+ n*0x4)
#define CORE_FIQSOURCE(n)       HWREG32(per_base_40000000 + 0x70+ n*0x4)

#define CORE_MAILBOX3_SET(n)    HWREG32(per_base_40000000 + 0x8C + n*0x10)
#define CORE_MAILBOX3_CLEAR(n)  HWREG32(per_base_40000000 + 0xCC + n*0x10)
#define CORE_MAILBOX2_SET(n)    HWREG32(per_base_40000000 + 0x88 + n*0x10)
#define CORE_MAILBOX2_CLEAR(n)  HWREG32(per_base_40000000 + 0xC8 + n*0x10)
#define CORE_MAILBOX1_SET(n)    HWREG32(per_base_40000000 + 0x84 + n*0x10)
#define CORE_MAILBOX1_CLEAR(n)  HWREG32(per_base_40000000 + 0xC4 + n*0x10)
#define CORE_MAILBOX0_SET(n)    HWREG32(per_base_40000000 + 0x80 + n*0x10)
#define CORE_MAILBOX0_CLEAR(n)  HWREG32(per_base_40000000 + 0xC0 + n*0x10)


/* UART PL011 */
#define UART_BASE                   (PER_BASE + PL011_UART_BASE_OFFSET)
//extern uint32_t uart_base_addr;
#define UART0_BASE                  (UART_BASE + 0x0)
#define UART2_BASE                  (UART_BASE + 0x400)
#define UART3_BASE                  (UART_BASE + 0x600)
#define UART4_BASE                  (UART_BASE + 0x800)
#define UART5_BASE                  (UART_BASE + 0xA00)
#define IRQ_AUX_UART                (96 + 29)
#define UART_REFERENCE_CLOCK        (48000000)

/* AUX */
//#define AUX_BASE_ADDR               (PER_BASE + AUX_BASE_OFFSET)
//extern uint32_t aux_addr;
//#define AUX_BASE                    (aux_addr + 0x0)

#define AUX_BASE                    (PER_BASE + AUX_BASE_OFFSET)
#define IRQ_PL011                   (96 + 57)

/* Peripheral IRQ OR-ing */
#define PACTL_CS_ADDR               (PER_BASE + PACTL_CS_OFFSET)
extern uint32_t     pactl_cs_base;
#define PACTL_CS                    HWREG32(pactl_cs_base)
typedef enum {
    IRQ_SPI0 = 0x00000000,
    IRQ_SPI1 = 0x00000002,
    IRQ_SPI2 = 0x00000004,
    IRQ_SPI3 = 0x00000008,
    IRQ_SPI4 = 0x00000010,
    IRQ_SPI5 = 0x00000020,
    IRQ_SPI6 = 0x00000040,
    IRQ_I2C0 = 0x00000100,
    IRQ_I2C1 = 0x00000200,
    IRQ_I2C2 = 0x00000400,
    IRQ_I2C3 = 0x00000800,
    IRQ_I2C4 = 0x00001000,
    IRQ_I2C5 = 0x00002000,
    IRQ_I2C6 = 0x00004000,
    IRQ_I2C7 = 0x00008000,
    IRQ_UART5 = 0x00010000,
    IRQ_UART4 = 0x00020000,
    IRQ_UART3 = 0x00040000,
    IRQ_UART2 = 0x00080000,
    IRQ_UART0 = 0x00100000
} PACTL_CS_VAL;

// 0x40, 0x44, 0x48, 0x4c: Core 0~3 Timers interrupt control
#define CORE0_TIMER_IRQ_CTRL        HWREG32(0xFF800040)
#define TIMER_IRQ                   30
#define NON_SECURE_TIMER_IRQ        (1 << 1)

//watchdog
#define WDT_BASE        (PER_BASE + 0x00100000)
extern uint32_t         wdt_base_addr;
#define PM_RSTC         HWREG32(wdt_base_addr + 0x1c)
#define PM_RSTS         HWREG32(wdt_base_addr + 0x20)
#define PM_WDOG         HWREG32(wdt_base_addr + 0x24)

#define PM_PASSWORD                 (0x5A000000)
#define PM_WDOG_TIME_SET            (0x000fffff)
#define PM_RSTS_HADWRH_SET          (0x00000040)
#define PM_RSTC_WRCFG_FULL_RESET    (0x00000020)
#define PM_RSTC_WRCFG_CLR           (0xffffffcf)
#define PM_RSTC_RESET               (0x00000102)

//timer
#define ST_BASE_OFFSET     (0x003000)
#define STIMER_BASE  (PER_BASE  + ST_BASE_OFFSET)
extern uint32_t stimer_base_addr;
#define STIMER_CS    __REG32(stimer_base_addr + 0x0000)
#define STIMER_CLO   __REG32(stimer_base_addr + 0x0004)
#define STIMER_CHI   __REG32(stimer_base_addr + 0x0008)
#define STIMER_C0    __REG32(stimer_base_addr + 0x000C)
#define STIMER_C1    __REG32(stimer_base_addr + 0x0010)
#define STIMER_C2    __REG32(stimer_base_addr + 0x0014)
#define STIMER_C3    __REG32(stimer_base_addr + 0x0018)

#define DELAY_MICROS(micros) \
    do{ \
 rt_uint32_t compare = STIMER_CLO + micros * 25; \
 while (STIMER_CLO < compare); \
    } while (0) \

//External Mass Media Controller (SD Card)
#define MMC0_BASE_ADDR    (PER_BASE+0x300000)
extern uint32_t mmc0_base_addr; 
#define MMC2_BASE_ADDR    (PER_BASE+0x340000)
extern uint32_t mmc2_base_addr;

//mac
#define MAC_REG                 (void *)(0xfd580000)
extern void *                   mac_reg_base_addr;

#define SEND_DATA_NO_CACHE      (0x08200000)
extern void *                   eth_send_no_cache;

#define RECV_DATA_NO_CACHE      (0x08400000)
extern void *                   eth_recv_no_cache;


#define IRQ_ARM_TIMER     64
#define IRQ_ARM_MAILBOX     65

/* For SMP IPI use MailBox0 */
#define IPI_MAILBOX_SET  CORE_MAILBOX0_SET
#define IPI_MAILBOX_CLEAR     CORE_MAILBOX0_CLEAR
#define IPI_MAILBOX_INT_MASK (0x01)

#endif

